#ifndef NUTSTORE_COMMAND_H
#define NUTSTORE_COMMAND_H 

#include <libnautilus-extension/nautilus-info-provider.h>
#include <libnautilus-extension/nautilus-file-info.h>

#define COMMAND_SERVER_PORT (19080)

G_BEGIN_DECLS

/**
 * General Command ResponseHandler
 */
typedef struct _NutstoreCommand NutstoreCommand;
typedef void (* NutstoreCommandRespHandler)(NutstoreCommand *cmd);

/**
 * Nutstore command
 */
struct _NutstoreCommand {
    // name and arguments
    gchar           *command_name;
    GHashTable      *command_args;

    // whether it's cancelled
    gboolean        cancelled;
 
    // the response handler. It accepts a command as input parameter. 
    // It should never free the command data. But it's responsible to 
    // release the user_data.
    NutstoreCommandRespHandler  handler;

    // whether the handler is called in client thread or in glib main loop by 
    // calling g_add_idle(). Not matter calling in client thread or in glib main
    // loop, the command data are always released by command client.
    gboolean        b_add_idle;
   
    // user data for response handler
    // it should be free in response handler
    gpointer        user_data;

    // The response
    GHashTable      *response;
};

/**
 * user data for file info command
 */
typedef struct {
    // callback arguments from Nautilus 
    NautilusInfoProvider    *provider;
    GClosure                *update_complete;
    NautilusFileInfo        *file;
} FileInfoUserData;

/**
 * hooks
 */
typedef void (*NutstoreCommandClientConnectionAttemptHook)(guint, gpointer);
typedef GHookFunc NutstoreCommandClientConnectHook;

/**
 * Command Client
 */
typedef struct {
    // Mutex to procted field "is_connected"
    GMutex      *mutex;

    // whether the client is connected to server 
    gboolean    is_connected;

    // async queue to hold request commands
    GAsyncQueue *cmd_queue; 

    // Three hook list:
    //   1) hook of connect attempt event
    //   2) hook of onconnect event
    //   3) hook of ondisconnect event
    GList       *conn_attempt_hooklist;
    GHookList   onconnect_hooklist;
    GHookList   ondisconnect_hooklist;
} NutstoreCommandClient;

/**
 * create command 
 */
NutstoreCommand *nutstore_command_new(NutstoreCommandRespHandler handler, 
        gboolean b_add_idle,
        gpointer user_data,
        const char *cmd_name, ...);

NutstoreCommand *nutstore_command_new_simple(const char *cmd_name);

/**
 * Setup command client
 */
void nutstore_command_client_setup(NutstoreCommandClient *ncc);

/** 
 * Start command client by creating a thread
 */
void nutstore_command_client_start(NutstoreCommandClient *ncc);

/**
 * Querry connecting status
 */
gboolean nutstore_command_client_is_connected(NutstoreCommandClient *ncc);

/**
 * Force the client reconnect to server
 */
void nutstore_command_client_force_reconnect(NutstoreCommandClient *ncc);

/**
 * Async request to client
 */
void nutstore_command_client_request(NutstoreCommandClient *ncc, NutstoreCommand *nc);

/**
 * send simple command to server
 */
void nutstore_command_client_send_simple_command(NutstoreCommandClient *ncc,
        const char *command);

/**
 * send command to server 
 */
void nutstore_command_client_send_command(NutstoreCommandClient *ncc, 
        NutstoreCommandRespHandler handler,
        gboolean b_add_idle,
        gpointer user_data,
        const char *command, ...);

/**
 * add hooks
 */
void nutstore_command_client_add_on_connect_hook(NutstoreCommandClient *ncc,
        NutstoreCommandClientConnectHook dhcch,
        gpointer ud);

void nutstore_command_client_add_on_disconnect_hook(NutstoreCommandClient *ncc,
        NutstoreCommandClientConnectHook dhcch,
        gpointer ud);

void nutstore_command_client_add_connection_attempt_hook(NutstoreCommandClient *ncc,
        NutstoreCommandClientConnectionAttemptHook dhcch,
        gpointer ud);

G_END_DECLS

#endif
